home *** CD-ROM | disk | FTP | other *** search
/ HAKERIS 11 / HAKERIS 11.ISO / linux / system / LinuxConsole 0.4 / linuxconsole0.4install-en.iso / guile0.4.lcm / share / guile / 1.6.0 / ice-9 / ftw.scm < prev    next >
Encoding:
Text File  |  2004-01-06  |  17.1 KB  |  385 lines

  1. ;;;; ftw.scm --- filesystem tree walk
  2.  
  3. ;;;;     Copyright (C) 2002 Free Software Foundation, Inc.
  4. ;;;;
  5. ;;;; This program is free software; you can redistribute it and/or modify
  6. ;;;; it under the terms of the GNU General Public License as published by
  7. ;;;; the Free Software Foundation; either version 2, or (at your option)
  8. ;;;; any later version.
  9. ;;;;
  10. ;;;; This program is distributed in the hope that it will be useful,
  11. ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. ;;;; GNU General Public License for more details.
  14. ;;;;
  15. ;;;; You should have received a copy of the GNU General Public License
  16. ;;;; along with this software; see the file COPYING.  If not, write to
  17. ;;;; the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  18. ;;;; Boston, MA 02111-1307 USA
  19. ;;;;
  20. ;;;; As a special exception, the Free Software Foundation gives permission
  21. ;;;; for additional uses of the text contained in its release of GUILE.
  22. ;;;;
  23. ;;;; The exception is that, if you link the GUILE library with other files
  24. ;;;; to produce an executable, this does not by itself cause the
  25. ;;;; resulting executable to be covered by the GNU General Public License.
  26. ;;;; Your use of that executable is in no way restricted on account of
  27. ;;;; linking the GUILE library code into it.
  28. ;;;;
  29. ;;;; This exception does not however invalidate any other reasons why
  30. ;;;; the executable file might be covered by the GNU General Public License.
  31. ;;;;
  32. ;;;; This exception applies only to the code released by the
  33. ;;;; Free Software Foundation under the name GUILE.  If you copy
  34. ;;;; code from other Free Software Foundation releases into a copy of
  35. ;;;; GUILE, as the General Public License permits, the exception does
  36. ;;;; not apply to the code that you add in this way.  To avoid misleading
  37. ;;;; anyone as to the status of such modified files, you must delete
  38. ;;;; this exception notice from them.
  39. ;;;;
  40. ;;;; If you write modifications of your own for GUILE, it is your choice
  41. ;;;; whether to permit this exception to apply to your modifications.
  42. ;;;; If you do not wish that, delete this exception notice.
  43.  
  44. ;;; Author: Thien-Thi Nguyen <ttn@gnu.org>
  45.  
  46. ;;; Commentary:
  47.  
  48. ;; Two procedures are provided: `ftw' and `nftw'.
  49.  
  50. ;; NOTE: The following description was adapted from the GNU libc info page, w/
  51. ;; significant modifications for a more "Schemey" interface.  Most noticible
  52. ;; are the inlining of `struct FTW *' parameters `base' and `level' and the
  53. ;; omission of `descriptors' parameters.
  54.  
  55. ;; * Types
  56. ;;
  57. ;;    The X/Open specification defines two procedures to process whole
  58. ;; hierarchies of directories and the contained files.  Both procedures
  59. ;; of this `ftw' family take as one of the arguments a callback procedure
  60. ;; which must be of these types.
  61. ;;
  62. ;;  - Data Type: __ftw_proc_t
  63. ;;           (lambda (filename statinfo flag) ...) => status
  64. ;;
  65. ;;      Type for callback procedures given to the `ftw' procedure.  The
  66. ;;      first parameter is a filename, the second parameter is the
  67. ;;      vector value as returned by calling `stat' on FILENAME.
  68. ;;
  69. ;;      The last parameter is a symbol giving more information about
  70. ;;      FILENAM.  It can have one of the following values:
  71. ;;
  72. ;;     `regular'
  73. ;;           The current item is a normal file or files which do not fit
  74. ;;           into one of the following categories.  This means
  75. ;;           especially special files, sockets etc.
  76. ;;
  77. ;;     `directory'
  78. ;;           The current item is a directory.
  79. ;;
  80. ;;     `invalid-stat'
  81. ;;           The `stat' call to fill the object pointed to by the second
  82. ;;           parameter failed and so the information is invalid.
  83. ;;
  84. ;;     `directory-not-readable'
  85. ;;           The item is a directory which cannot be read.
  86. ;;
  87. ;;     `symlink'
  88. ;;           The item is a symbolic link.  Since symbolic links are
  89. ;;           normally followed seeing this value in a `ftw' callback
  90. ;;           procedure means the referenced file does not exist.  The
  91. ;;           situation for `nftw' is different.
  92. ;;
  93. ;;  - Data Type: __nftw_proc_t
  94. ;;           (lambda (filename statinfo flag base level) ...) => status
  95. ;;
  96. ;;      The first three arguments have the same as for the
  97. ;;      `__ftw_proc_t' type.  A difference is that for the third
  98. ;;      argument some additional values are defined to allow finer
  99. ;;      differentiation:
  100. ;;
  101. ;;     `directory-processed'
  102. ;;           The current item is a directory and all subdirectories have
  103. ;;           already been visited and reported.  This flag is returned
  104. ;;           instead of `directory' if the `depth' flag is given to
  105. ;;           `nftw' (see below).
  106. ;;
  107. ;;     `stale-symlink'
  108. ;;           The current item is a stale symbolic link.  The file it
  109. ;;           points to does not exist.
  110. ;;
  111. ;;      The last two parameters are described below.  They contain
  112. ;;      information to help interpret FILENAME and give some information
  113. ;;      about current state of the traversal of the directory hierarchy.
  114. ;;
  115. ;;     `base'
  116. ;;           The value specifies which part of the filename argument
  117. ;;           given in the first parameter to the callback procedure is
  118. ;;           the name of the file.  The rest of the string is the path
  119. ;;           to locate the file.  This information is especially
  120. ;;           important if the `chdir' flag for `nftw' was set since then
  121. ;;           the current directory is the one the current item is found
  122. ;;           in.
  123. ;;
  124. ;;     `level'
  125. ;;           While processing the directory the procedures tracks how
  126. ;;           many directories have been examined to find the current
  127. ;;           item.  This nesting level is 0 for the item given starting
  128. ;;           item (file or directory) and is incremented by one for each
  129. ;;           entered directory.
  130. ;;
  131. ;; * Procedure: (ftw filename proc . options)
  132. ;;   Do a filesystem tree walk starting at FILENAME using PROC.
  133. ;;
  134. ;;   The `ftw' procedure calls the callback procedure given in the
  135. ;;   parameter PROC for every item which is found in the directory
  136. ;;   specified by FILENAME and all directories below.  The procedure
  137. ;;   follows symbolic links if necessary but does not process an item
  138. ;;   twice.  If FILENAME names no directory this item is the only
  139. ;;   object reported by calling the callback procedure.
  140. ;;
  141. ;;   The filename given to the callback procedure is constructed by
  142. ;;   taking the FILENAME parameter and appending the names of all
  143. ;;   passed directories and then the local file name.  So the
  144. ;;   callback procedure can use this parameter to access the file.
  145. ;;   Before the callback procedure is called `ftw' calls `stat' for
  146. ;;   this file and passes the information up to the callback
  147. ;;   procedure.  If this `stat' call was not successful the failure is
  148. ;;   indicated by setting the flag argument of the callback procedure
  149. ;;   to `invalid-stat'.  Otherwise the flag is set according to the
  150. ;;   description given in the description of `__ftw_proc_t' above.
  151. ;;
  152. ;;   The callback procedure is expected to return non-#f to indicate
  153. ;;   that no error occurred and the processing should be continued.
  154. ;;   If an error occurred in the callback procedure or the call to
  155. ;;   `ftw' shall return immediately the callback procedure can return
  156. ;;   #f.  This is the only correct way to stop the procedure.  The
  157. ;;   program must not use `throw' or similar techniques to continue
  158. ;;   the program in another place.  [Can we relax this? --ttn]
  159. ;;
  160. ;;   The return value of the `ftw' procedure is #t if all callback
  161. ;;   procedure calls returned #t and all actions performed by the
  162. ;;   `ftw' succeeded.  If some procedure call failed (other than
  163. ;;   calling `stat' on an item) the procedure returns #f.  If a
  164. ;;   callback procedure returns a value other than #t this value is
  165. ;;   returned as the return value of `ftw'.
  166. ;;
  167. ;; * Procedure: (nftw filename proc . control-flags)
  168. ;;   Do a new-style filesystem tree walk starting at FILENAME using PROC.
  169. ;;   Various optional CONTROL-FLAGS alter the default behavior.
  170. ;;
  171. ;;   The `nftw' procedures works like the `ftw' procedures.  It calls
  172. ;;   the callback procedure PROC for all items it finds in the
  173. ;;   directory FILENAME and below.
  174. ;;
  175. ;;   The differences are that for one the callback procedure is of a
  176. ;;   different type.  It takes also `base' and `level' parameters as
  177. ;;   described above.
  178. ;;
  179. ;;   The second difference is that `nftw' takes additional optional
  180. ;;   arguments which are zero or more of the following symbols:
  181. ;;
  182. ;;   physical'
  183. ;;        While traversing the directory symbolic links are not
  184. ;;        followed.  I.e., if this flag is given symbolic links are
  185. ;;        reported using the `symlink' value for the type parameter
  186. ;;        to the callback procedure.  Please note that if this flag is
  187. ;;        used the appearance of `symlink' in a callback procedure
  188. ;;        does not mean the referenced file does not exist.  To
  189. ;;        indicate this the extra value `stale-symlink' exists.
  190. ;;
  191. ;;   mount'
  192. ;;        The callback procedure is only called for items which are on
  193. ;;        the same mounted filesystem as the directory given as the
  194. ;;        FILENAME parameter to `nftw'.
  195. ;;
  196. ;;   chdir'
  197. ;;        If this flag is given the current working directory is
  198. ;;        changed to the directory containing the reported object
  199. ;;        before the callback procedure is called.
  200. ;;
  201. ;;   depth'
  202. ;;        If this option is given the procedure visits first all files
  203. ;;        and subdirectories before the callback procedure is called
  204. ;;        for the directory itself (depth-first processing).  This
  205. ;;        also means the type flag given to the callback procedure is
  206. ;;        `directory-processed' and not `directory'.
  207. ;;
  208. ;;   The return value is computed in the same way as for `ftw'.
  209. ;;   `nftw' returns #t if no failure occurred in `nftw' and all
  210. ;;   callback procedure call return values are also #t.  For internal
  211. ;;   errors such as memory problems the error `ftw-error' is thrown.
  212. ;;   If the return value of a callback invocation is not #t this
  213. ;;   very same value is returned.
  214.  
  215. ;;; Code:
  216.  
  217. (define-module (ice-9 ftw)
  218.   :export (ftw nftw))
  219.  
  220. (define (directory-files dir)
  221.   (let ((dir-stream (opendir dir)))
  222.     (let loop ((new (readdir dir-stream))
  223.                (acc '()))
  224.       (if (eof-object? new)
  225.           acc
  226.           (loop (readdir dir-stream)
  227.                 (if (or (string=? "."  new)             ;;; ignore
  228.                         (string=? ".." new))            ;;; ignore
  229.                     acc
  230.                     (cons new acc)))))))
  231.  
  232. (define (pathify . nodes)
  233.   (let loop ((nodes nodes)
  234.              (result ""))
  235.     (if (null? nodes)
  236.         (or (and (string=? "" result) "")
  237.             (substring result 1 (string-length result)))
  238.         (loop (cdr nodes) (string-append result "/" (car nodes))))))
  239.  
  240. (define (abs? filename)
  241.   (char=? #\/ (string-ref filename 0)))
  242.  
  243. (define (visited?-proc size)
  244.   (let ((visited (make-hash-table size)))
  245.     (lambda (s)
  246.       (and s (let ((ino (stat:ino s)))
  247.                (or (hash-ref visited ino)
  248.                    (begin
  249.                      (hash-set! visited ino #t)
  250.                      #f)))))))
  251.  
  252. (define (stat-dir-readable?-proc uid gid)
  253.   (let ((uid (getuid))
  254.         (gid (getgid)))
  255.     (lambda (s)
  256.       (let* ((perms (stat:perms s))
  257.              (perms-bit-set? (lambda (mask)
  258.                                (not (= 0 (logand mask perms))))))
  259.         (or (and (= uid (stat:uid s))
  260.                  (perms-bit-set? #o400))
  261.             (and (= gid (stat:gid s))
  262.                  (perms-bit-set? #o040))
  263.             (perms-bit-set? #o004))))))
  264.  
  265. (define (stat&flag-proc dir-readable? . control-flags)
  266.   (let* ((directory-flag (if (memq 'depth control-flags)
  267.                              'directory-processed
  268.                              'directory))
  269.          (stale-symlink-flag (if (memq 'nftw-style control-flags)
  270.                                  'stale-symlink
  271.                                  'symlink))
  272.          (physical? (memq 'physical control-flags))
  273.          (easy-flag (lambda (s)
  274.                       (let ((type (stat:type s)))
  275.                         (if (eq? 'directory type)
  276.                             (if (dir-readable? s)
  277.                                 directory-flag
  278.                                 'directory-not-readable)
  279.                             'regular)))))
  280.     (lambda (name)
  281.       (let ((s (false-if-exception (lstat name))))
  282.         (cond ((not s)
  283.                (values s 'invalid-stat))
  284.               ((eq? 'symlink (stat:type s))
  285.                (let ((s-follow (false-if-exception (stat name))))
  286.                  (cond ((not s-follow)
  287.                         (values s stale-symlink-flag))
  288.                        ((and s-follow physical?)
  289.                         (values s 'symlink))
  290.                        ((and s-follow (not physical?))
  291.                         (values s-follow (easy-flag s-follow))))))
  292.               (else (values s (easy-flag s))))))))
  293.  
  294. (define (clean name)
  295.   (let ((last-char-index (1- (string-length name))))
  296.     (if (char=? #\/ (string-ref name last-char-index))
  297.         (substring name 0 last-char-index)
  298.         name)))
  299.  
  300. (define (ftw filename proc . options)
  301.   (let* ((visited? (visited?-proc (cond ((memq 'hash-size options) => cadr)
  302.                                         (else 211))))
  303.          (stat&flag (stat&flag-proc
  304.                      (stat-dir-readable?-proc (getuid) (getgid)))))
  305.     (letrec ((go (lambda (fullname)
  306.                    (call-with-values (lambda () (stat&flag fullname))
  307.                      (lambda (s flag)
  308.                        (or (visited? s)
  309.                            (let ((ret (proc fullname s flag))) ; callback
  310.                              (or (eq? #t ret)
  311.                                  (throw 'ftw-early-exit ret))
  312.                              (and (eq? 'directory flag)
  313.                                   (for-each
  314.                                    (lambda (child)
  315.                                      (go (pathify fullname child)))
  316.                                    (directory-files fullname)))
  317.                              #t)))))))
  318.       (catch 'ftw-early-exit
  319.              (lambda () (go (clean filename)))
  320.              (lambda (key val) val)))))
  321.  
  322. (define (nftw filename proc . control-flags)
  323.   (let* ((od (getcwd))                  ; orig dir
  324.          (odev (let ((s (false-if-exception (lstat filename))))
  325.                  (if s (stat:dev s) -1)))
  326.          (same-dev? (if (memq 'mount control-flags)
  327.                         (lambda (s) (= (stat:dev s) odev))
  328.                         (lambda (s) #t)))
  329.          (base-sub (lambda (name base) (substring name 0 base)))
  330.          (maybe-cd (if (memq 'chdir control-flags)
  331.                        (if (abs? filename)
  332.                            (lambda (fullname base)
  333.                              (or (= 0 base)
  334.                                  (chdir (base-sub fullname base))))
  335.                            (lambda (fullname base)
  336.                              (chdir
  337.                               (pathify od (base-sub fullname base)))))
  338.                        (lambda (fullname base) #t)))
  339.          (maybe-cd-back (if (memq 'chdir control-flags)
  340.                             (lambda () (chdir od))
  341.                             (lambda () #t)))
  342.          (depth-first? (memq 'depth control-flags))
  343.          (visited? (visited?-proc
  344.                     (cond ((memq 'hash-size control-flags) => cadr)
  345.                           (else 211))))
  346.          (has-kids? (if depth-first?
  347.                         (lambda (flag) (eq? flag 'directory-processed))
  348.                         (lambda (flag) (eq? flag 'directory))))
  349.          (stat&flag (apply stat&flag-proc
  350.                            (stat-dir-readable?-proc (getuid) (getgid))
  351.                            (cons 'nftw-style control-flags))))
  352.     (letrec ((go (lambda (fullname base level)
  353.                    (call-with-values (lambda () (stat&flag fullname))
  354.                      (lambda (s flag)
  355.                        (letrec ((self (lambda ()
  356.                                         (maybe-cd fullname base)
  357.                                         ;; the callback
  358.                                         (let ((ret (proc fullname s flag
  359.                                                          base level)))
  360.                                           (maybe-cd-back)
  361.                                           (or (eq? #t ret)
  362.                                               (throw 'nftw-early-exit ret)))))
  363.                                 (kids (lambda ()
  364.                                         (and (has-kids? flag)
  365.                                              (for-each
  366.                                               (lambda (child)
  367.                                                 (go (pathify fullname child)
  368.                                                     (1+ (string-length
  369.                                                          fullname))
  370.                                                     (1+ level)))
  371.                                               (directory-files fullname))))))
  372.                          (or (visited? s)
  373.                              (not (same-dev? s))
  374.                              (if depth-first?
  375.                                  (begin (kids) (self))
  376.                                  (begin (self) (kids)))))))
  377.                    #t)))
  378.       (let ((ret (catch 'nftw-early-exit
  379.                         (lambda () (go (clean filename) 0 0))
  380.                         (lambda (key val) val))))
  381.         (chdir od)
  382.         ret))))
  383.  
  384. ;;; ftw.scm ends here
  385.